<!DOCTYPE html>
<html lang="zh_cn">
<head>
          <title>来玩魔王的咚</title>
        <meta name="viewport" content="width=device-width, initial-scale=1" />
        <meta charset="utf-8" />
        <!-- twitter card metadata -->
<meta name="twitter:card" content="summary">
<meta name="twitter:image" content="/images/mowang.png">
<meta name="twitter:site" content="">
<meta name="twitter:title" content="Python functools 模块">
<meta name="twitter:description" content="<p>reduce、map和filter，归约函数；wraps保留被装饰函数属性；partial冻结参数；lru_cache函数记忆；singledispatch单分派泛函数；一个能接收可选参数的装饰器：仅限关键字参数，run_time装饰器</p>">
        <!-- OG Tags -->
<meta property="og:url" content="/python-moudle-functools.html"/>
<meta property="og:title" content="来玩魔王的咚 | Python functools 模块" />
<meta property="og:description" content="<p>reduce、map和filter，归约函数；wraps保留被装饰函数属性；partial冻结参数；lru_cache函数记忆；singledispatch单分派泛函数；一个能接收可选参数的装饰器：仅限关键字参数，run_time装饰器</p>" />
        <!-- favicon -->
        <link rel="icon" type="image/png" href="/images/mowang.png">
        <!-- moment.js for date formatting -->
        <script src="/theme/js/moment.js"></script>
        <!-- css -->
        <link rel="stylesheet" type="text/css" href="/theme/css/main.css" />
        <!-- 左边的menu，如果页面高度不够，就跟着滚动，否则文章分类显示不全 -->
        <link rel="stylesheet" type="text/css" href="/theme/css/mycss/menu.css" />
		<script>
			
                /*! grunt-grunticon Stylesheet Loader - v2.1.2 | https://github.com/filamentgroup/grunticon | (c) 2015 Scott Jehl, Filament Group, Inc. | MIT license. */
    
    (function(e){function t(t,n,r,o){"use strict";function a(){for(var e,n=0;u.length>n;n++)u[n].href&&u[n].href.indexOf(t)>-1&&(e=!0);e?i.media=r||"all":setTimeout(a)}var i=e.document.createElement("link"),l=n||e.document.getElementsByTagName("script")[0],u=e.document.styleSheets;return i.rel="stylesheet",i.href=t,i.media="only x",i.onload=o||null,l.parentNode.insertBefore(i,l),a(),i}var n=function(r,o){"use strict";if(r&&3===r.length){var a=e.navigator,i=e.Image,l=!(!document.createElementNS||!document.createElementNS("http://www.w3.org/2000/svg","svg").createSVGRect||!document.implementation.hasFeature("http://www.w3.org/TR/SVG11/feature#Image","1.1")||e.opera&&-1===a.userAgent.indexOf("Chrome")||-1!==a.userAgent.indexOf("Series40")),u=new i;u.onerror=function(){n.method="png",n.href=r[2],t(r[2])},u.onload=function(){var e=1===u.width&&1===u.height,a=r[e&&l?0:e?1:2];n.method=e&&l?"svg":e?"datapng":"png",n.href=a,t(a,null,null,o)},u.src="",document.documentElement.className+=" grunticon"}};n.loadCSS=t,e.grunticon=n})(this);(function(e,t){"use strict";var n=t.document,r="grunticon:",o=function(e){if(n.attachEvent?"complete"===n.readyState:"loading"!==n.readyState)e();else{var t=!1;n.addEventListener("readystatechange",function(){t||(t=!0,e())},!1)}},a=function(e){return t.document.querySelector('link[href$="'+e+'"]')},c=function(e){var t,n,o,a,c,i,u={};if(t=e.sheet,!t)return u;n=t.cssRules?t.cssRules:t.rules;for(var l=0;n.length>l;l++)o=n[l].cssText,a=r+n[l].selectorText,c=o.split(");")[0].match(/US\-ASCII\,([^"']+)/),c&&c[1]&&(i=decodeURIComponent(c[1]),u[a]=i);return u},i=function(e){var t,o,a;o="data-grunticon-embed";for(var c in e)if(a=c.slice(r.length),t=n.querySelectorAll(a+"["+o+"]"),t.length)for(var i=0;t.length>i;i++)t[i].innerHTML=e[c],t[i].style.backgroundImage="none",t[i].removeAttribute(o);return t},u=function(t){"svg"===e.method&&o(function(){i(c(a(e.href))),"function"==typeof t&&t()})};e.embedIcons=i,e.getCSS=a,e.getIcons=c,e.ready=o,e.svgLoadedCallback=u,e.embedSVG=u})(grunticon,this);
                
                grunticon(["/theme/css/icons.data.svg.css", "/theme/css/icons.data.png.css", "/theme/css/icons.fallback.css"]);
            </script>
        <noscript><link href="/theme/css/icons.fallback.css" rel="stylesheet"></noscript>
        <!-- menu toggle javascript -->
        <script type="text/javascript">
            document.addEventListener("DOMContentLoaded", initMenu);
            
            function initMenu(){
                var menu = document.getElementById("menu");
                var menulink = document.getElementById("menu-link");
                menulink.addEventListener("click", function toggleMenu(){
                        window.event.preventDefault();
                        menulink.classList.toggle('active');
                        menu.classList.toggle('active');              
                    });
            };
        </script>
        <!-- 不蒜子 -->
        <script async src="//busuanzi.ibruce.info/busuanzi/2.3/busuanzi.pure.mini.js"></script>

    <meta name="description" content="<p>reduce、map和filter，归约函数；wraps保留被装饰函数属性；partial冻结参数；lru_cache函数记忆；singledispatch单分派泛函数；一个能接收可选参数的装饰器：仅限关键字参数，run_time装饰器</p>" />

    <meta name="tags" content="装饰器" />
    <meta name="tags" content="python模块" />
  <!-- 替换部分base的样式，看文章时，要再宽一点，右边有很多空间可以撑开 -->
  <link rel="stylesheet" type="text/css" href="/theme/css/mycss/article.css" />

</head>
<body>
    <div role="banner" id="masthead">
        <header>
            <a href="/"><img src="/images/mowang.png" alt="McManus Logo"></a>
                <h1>来玩魔王的咚@骑士救兵</h1>
            <a href="#menu" id="menu-link">more stuff</a>
            <nav id="menu">
                <ul>
                        <li><a href="/tags">tags</a></li>
                            <li><a href="/category/cloud.html">Cloud</a></li>
                            <li><a href="/category/docker.html">Docker</a></li>
                            <li><a href="/category/go.html">Go</a></li>
                            <li><a href="/category/linux.html">Linux</a></li>
                            <li class="active"><a href="/category/python.html">Python</a></li>
                            <li><a href="/category/xue-xi-bi-ji.html">学习笔记</a></li>
                            <li><a href="/category/yun-wei-zi-dong-hua.html">运维自动化</a></li>
                </ul>
            </nav>
        </header>
    </div>
        <div class="page" role="main">
  <div class="article" role="article">
    <article>
        <footer>
            <a name="top"></a>
            <p>
              <time datetime=" 2020-08-25 13:00:00+08:00">
                <script>document.write(moment('2020-08-25 13:00:00+08:00').format('LL'));</script>
              </time>
              ~
              <time datetime=" 2020-08-25 13:00:00+08:00">
                <script>document.write(moment('2020-08-27 15:00:00+08:00').format('LL'));</script>
              </time>
            </p>
        </footer>
        <header>
          <h2>
            Python functools 模块
          </h2>
        </header>
      <div class="content">
         <div class="toc">
<ul>
<li><a href="#reducemap-he-filter">reduce、map和filter</a><ul>
<li><a href="#sheng-cheng-qi-biao-da-shi-ti-dai-map-he-filter">生成器表达式替代map和filter</a></li>
<li><a href="#reduce-gui-yue-han-shu">reduce 归约函数</a></li>
</ul>
</li>
<li><a href="#wraps-bao-liu-bei-zhuang-shi-han-shu-shu-xing">wraps 保留被装饰函数属性</a></li>
<li><a href="#partial-partialmethod-dong-jie-can-shu">partial partialmethod 冻结参数</a></li>
<li><a href="#lru_cache-han-shu-ji-yi">lru_cache 函数记忆</a><ul>
<li><a href="#shi-li-yan-shi">示例演示</a></li>
<li><a href="#can-shu-shuo-ming">参数说明</a></li>
</ul>
</li>
<li><a href="#singledispatch-dan-fen-pai-fan-han-shu">singledispatch 单分派泛函数</a><ul>
<li><a href="#shi-li-dai-ma">示例代码</a></li>
<li><a href="#jian-yi-shi-yong-chou-xiang-ji-lei">建议使用抽象基类</a></li>
<li><a href="#shi-yong-singledispatch-de-you-shi">使用singledispatch的优势</a></li>
</ul>
</li>
<li><a href="#yi-ge-neng-jie-shou-ke-xuan-can-shu-de-zhuang-shi-qi">一个能接收可选参数的装饰器</a><ul>
<li><a href="#jin-xian-guan-jian-zi-can-shu">仅限关键字参数</a></li>
<li><a href="#dai-can-shu-de-run_time-zhuang-shi-qi">带参数的run_time装饰器</a></li>
<li><a href="#xiu-gai-run_time-zhuang-shi-qi">修改run_time装饰器</a></li>
<li><a href="#wan-cheng-run_time-zhuang-shi-qi">完成run_time装饰器</a></li>
</ul>
</li>
</ul>
</div>
<p>标准库：<a href="https://docs.python.org/zh-cn/3.7/library/functools.html">https://docs.python.org/zh-cn/3.7/library/functools.html</a><br>
functools 模块，应用于高阶函数。<br>
<strong>高阶函数</strong>：接受函数为参数，或者把函数作为结果返回的函数就是高阶函数(higher-order function)。即参数或返回值（至少有一个或两者都是）为其他函数的函数。  </p>
<h3 id="reducemap-he-filter"><a class="toclink" href="#reducemap-he-filter">reduce、map和filter</a></h3>
<p>模块中只有reduce，但是这3兄弟的情况一起讲。<br>
函数式语言通常会提供map、filter和reduce三个高阶函数（有时使用不同的名称）。在Python3中，map和filter还是内置函数。reduce原本也是内置函数，但现在被放到了functools模块里了。  </p>
<h4 id="sheng-cheng-qi-biao-da-shi-ti-dai-map-he-filter"><a class="toclink" href="#sheng-cheng-qi-biao-da-shi-ti-dai-map-he-filter">生成器表达式替代map和filter</a></h4>
<p>虽然这些函数很实用，但是在多数使用场景下都已经有了更好的替代品。<br>
现在有了列表生成式和生成器表达式，它们具有map和filter这两个函数的功能：</p>
<div class="highlight"><pre><span></span><code><span class="o">&gt;&gt;&gt;</span> <span class="nb">list</span><span class="p">(</span><span class="nb">map</span><span class="p">(</span><span class="k">lambda</span> <span class="n">x</span><span class="p">:</span> <span class="n">x</span> <span class="o">**</span> <span class="mi">2</span><span class="p">,</span> <span class="nb">range</span><span class="p">(</span><span class="mi">10</span><span class="p">)))</span>
<span class="p">[</span><span class="mi">0</span><span class="p">,</span> <span class="mi">1</span><span class="p">,</span> <span class="mi">4</span><span class="p">,</span> <span class="mi">9</span><span class="p">,</span> <span class="mi">16</span><span class="p">,</span> <span class="mi">25</span><span class="p">,</span> <span class="mi">36</span><span class="p">,</span> <span class="mi">49</span><span class="p">,</span> <span class="mi">64</span><span class="p">,</span> <span class="mi">81</span><span class="p">]</span>
<span class="o">&gt;&gt;&gt;</span> <span class="p">[</span> <span class="n">i</span> <span class="o">**</span> <span class="mi">2</span> <span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="mi">10</span><span class="p">)</span> <span class="p">]</span>
<span class="p">[</span><span class="mi">0</span><span class="p">,</span> <span class="mi">1</span><span class="p">,</span> <span class="mi">4</span><span class="p">,</span> <span class="mi">9</span><span class="p">,</span> <span class="mi">16</span><span class="p">,</span> <span class="mi">25</span><span class="p">,</span> <span class="mi">36</span><span class="p">,</span> <span class="mi">49</span><span class="p">,</span> <span class="mi">64</span><span class="p">,</span> <span class="mi">81</span><span class="p">]</span>
<span class="o">&gt;&gt;&gt;</span> <span class="nb">list</span><span class="p">(</span><span class="nb">map</span><span class="p">(</span><span class="k">lambda</span> <span class="n">x</span><span class="p">:</span> <span class="n">x</span> <span class="o">**</span> <span class="mi">2</span><span class="p">,</span> <span class="nb">filter</span><span class="p">(</span><span class="k">lambda</span> <span class="n">n</span><span class="p">:</span> <span class="n">n</span> <span class="o">%</span> <span class="mi">2</span> <span class="o">==</span> <span class="mi">0</span><span class="p">,</span> <span class="nb">range</span><span class="p">(</span><span class="mi">10</span><span class="p">))))</span>
<span class="p">[</span><span class="mi">0</span><span class="p">,</span> <span class="mi">4</span><span class="p">,</span> <span class="mi">16</span><span class="p">,</span> <span class="mi">36</span><span class="p">,</span> <span class="mi">64</span><span class="p">]</span>
<span class="o">&gt;&gt;&gt;</span> <span class="p">[</span> <span class="n">i</span> <span class="o">**</span> <span class="mi">2</span> <span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="mi">10</span><span class="p">)</span> <span class="k">if</span> <span class="n">i</span> <span class="o">%</span> <span class="mi">2</span> <span class="o">==</span><span class="mi">0</span> <span class="p">]</span>
<span class="p">[</span><span class="mi">0</span><span class="p">,</span> <span class="mi">4</span><span class="p">,</span> <span class="mi">16</span><span class="p">,</span> <span class="mi">36</span><span class="p">,</span> <span class="mi">64</span><span class="p">]</span>
<span class="o">&gt;&gt;&gt;</span> 
</code></pre></div>

<p>在python3中，map和filter返回生成器，上面的例子中，只要把列表生成式的中括号替换为小括号，就变成了生成器表达式。因此，现在生成器表达式是map和filter函数的直接替代品。<br>
使用生成器表达式，提高了可读性，并且避免了使用lambda表达式。  </p>
<h4 id="reduce-gui-yue-han-shu"><a class="toclink" href="#reduce-gui-yue-han-shu">reduce 归约函数</a></h4>
<p><strong>归约函数</strong>：把某个操作，连续应用到序列的元素上，累积之前的结果，把一系列值规约成一个值。  </p>
<p>首先是加法操作，这个是可以用内置函数sum来替代的。并且可读性和性能都更好：</p>
<div class="highlight"><pre><span></span><code><span class="o">&gt;&gt;&gt;</span> <span class="kn">from</span> <span class="nn">functools</span> <span class="kn">import</span> <span class="n">reduce</span>
<span class="o">&gt;&gt;&gt;</span> <span class="n">reduce</span><span class="p">(</span><span class="k">lambda</span> <span class="n">x</span><span class="p">,</span> <span class="n">y</span><span class="p">:</span> <span class="n">x</span> <span class="o">+</span> <span class="n">y</span><span class="p">,</span> <span class="nb">range</span><span class="p">(</span><span class="mi">101</span><span class="p">))</span>
<span class="mi">5050</span>
<span class="o">&gt;&gt;&gt;</span> <span class="kn">from</span> <span class="nn">operator</span> <span class="kn">import</span> <span class="n">add</span>
<span class="o">&gt;&gt;&gt;</span> <span class="n">add</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span><span class="mi">2</span><span class="p">)</span>
<span class="mi">3</span>
<span class="o">&gt;&gt;&gt;</span> <span class="n">reduce</span><span class="p">(</span><span class="n">add</span><span class="p">,</span> <span class="nb">range</span><span class="p">(</span><span class="mi">101</span><span class="p">))</span>
<span class="mi">5050</span>
<span class="o">&gt;&gt;&gt;</span> <span class="nb">sum</span><span class="p">(</span><span class="nb">range</span><span class="p">(</span><span class="mi">101</span><span class="p">))</span>
<span class="mi">5050</span>
<span class="o">&gt;&gt;&gt;</span> 
</code></pre></div>

<p>不必自己写加法的函数，operator库里各种操作符都有对应的函数,这里用了operator.add。<br>
这里sum也是归约函数，在求和操作时，可以用来替代reduce。<br>
虽然是求和，但是reduce的计算其实是这样的：<code>((((1+2)+3)+4)+5)</code>，下面会再展开。<br>
另外，内置的all和any也是归约函数，遇到类似的操作，比用reducee更好。  </p>
<p>有些操作没有现成的归约函数可用，就需要用到reduce。比如乘法操作：</p>
<div class="highlight"><pre><span></span><code><span class="o">&gt;&gt;&gt;</span> <span class="kn">from</span> <span class="nn">functools</span> <span class="kn">import</span> <span class="n">reduce</span>
<span class="o">&gt;&gt;&gt;</span> <span class="kn">from</span> <span class="nn">operator</span> <span class="kn">import</span> <span class="n">mul</span>
<span class="o">&gt;&gt;&gt;</span> <span class="n">reduce</span><span class="p">(</span><span class="n">mul</span><span class="p">,</span> <span class="nb">range</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="mi">11</span><span class="p">))</span>
<span class="mi">3628800</span>
<span class="o">&gt;&gt;&gt;</span> 
</code></pre></div>

<p>从1开始，就是阶乘了。10! = 3628800  </p>
<p>reduce的计算方式如下描述：<br>
第一次取第一个元素为x和下一个元素y进行计算。计算的结果作为下一次计算的x，再和下一个元素计算。<br>
随便试一个复杂一些算式：</p>
<div class="highlight"><pre><span></span><code><span class="o">&gt;&gt;&gt;</span> <span class="n">reduce</span><span class="p">(</span><span class="k">lambda</span> <span class="n">x</span><span class="p">,</span> <span class="n">y</span><span class="p">:</span> <span class="n">x</span><span class="o">//</span><span class="mi">10</span> <span class="o">+</span> <span class="n">y</span><span class="o">%</span><span class="mi">10</span><span class="p">,</span> <span class="p">[</span><span class="mi">12</span><span class="p">,</span> <span class="mi">34</span><span class="p">,</span> <span class="mi">56</span><span class="p">])</span>
<span class="mi">6</span>
<span class="o">&gt;&gt;&gt;</span> <span class="p">(</span><span class="mi">12</span> <span class="o">//</span> <span class="mi">10</span> <span class="o">+</span> <span class="mi">34</span> <span class="o">%</span> <span class="mi">10</span><span class="p">)</span> <span class="o">//</span> <span class="mi">10</span> <span class="o">+</span> <span class="mi">56</span> <span class="o">%</span> <span class="mi">10</span>
<span class="mi">6</span>
<span class="o">&gt;&gt;&gt;</span> <span class="n">reduce</span><span class="p">(</span><span class="k">lambda</span> <span class="n">x</span><span class="p">,</span> <span class="n">y</span><span class="p">:</span> <span class="n">x</span><span class="o">%</span><span class="mi">10</span> <span class="o">+</span> <span class="n">y</span><span class="o">//</span><span class="mi">10</span><span class="p">,</span> <span class="p">[</span><span class="mi">12</span><span class="p">,</span> <span class="mi">34</span><span class="p">,</span> <span class="mi">56</span><span class="p">])</span>
<span class="mi">10</span>
<span class="o">&gt;&gt;&gt;</span> <span class="p">(</span><span class="mi">12</span> <span class="o">%</span> <span class="mi">10</span> <span class="o">+</span> <span class="mi">34</span> <span class="o">//</span> <span class="mi">10</span><span class="p">)</span> <span class="o">%</span> <span class="mi">10</span> <span class="o">+</span> <span class="mi">56</span> <span class="o">//</span> <span class="mi">10</span>
<span class="mi">10</span>
<span class="o">&gt;&gt;&gt;</span> 
</code></pre></div>

<p>//是取整，%是取余。  </p>
<p>这里的归约是从最前面2个元素开始操作，操作完后将结果作为第一个元素，再把下一个元素取出。再次对2个元素进行操作。直到最后一个元素。<br>
不要和map搞混了，map是依次先对每一个元素独立的进行相同的操作。然后可以再用归约，比如可以对map后的所有元素求和。  </p>
<div class="highlight"><pre><span></span><code><span class="o">&gt;&gt;&gt;</span> <span class="kn">from</span> <span class="nn">functools</span> <span class="kn">import</span> <span class="n">reduce</span>
<span class="o">&gt;&gt;&gt;</span> <span class="kn">from</span> <span class="nn">operator</span> <span class="kn">import</span> <span class="n">add</span>
<span class="o">&gt;&gt;&gt;</span> <span class="n">reduce</span><span class="p">(</span><span class="n">add</span><span class="p">,</span> <span class="nb">map</span><span class="p">(</span><span class="k">lambda</span> <span class="n">x</span><span class="p">:</span> <span class="n">x</span><span class="o">*</span><span class="mi">2</span><span class="p">,</span> <span class="nb">range</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="mi">11</span><span class="p">)))</span>
<span class="mi">110</span>
<span class="o">&gt;&gt;&gt;</span> <span class="nb">sum</span><span class="p">([</span><span class="n">i</span><span class="o">*</span><span class="mi">2</span> <span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="mi">11</span><span class="p">)])</span>
<span class="mi">110</span>
<span class="o">&gt;&gt;&gt;</span> <span class="n">reduce</span><span class="p">(</span><span class="k">lambda</span> <span class="n">x</span><span class="p">,</span> <span class="n">y</span><span class="p">:</span> <span class="n">x</span><span class="o">*</span><span class="mi">2</span> <span class="o">+</span> <span class="n">y</span><span class="o">*</span><span class="mi">2</span><span class="p">,</span> <span class="nb">range</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="mi">11</span><span class="p">))</span>
<span class="mi">3560</span>
<span class="o">&gt;&gt;&gt;</span> <span class="n">reduce</span><span class="p">(</span><span class="k">lambda</span> <span class="n">x</span><span class="p">,</span> <span class="n">y</span><span class="p">:</span> <span class="n">x</span><span class="o">*</span><span class="mi">2</span> <span class="o">+</span> <span class="n">y</span><span class="o">*</span><span class="mi">2</span><span class="p">,</span> <span class="nb">range</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="mi">5</span><span class="p">))</span>
<span class="mi">44</span>
<span class="o">&gt;&gt;&gt;</span> <span class="p">(((</span><span class="mi">1</span><span class="o">*</span><span class="mi">2</span><span class="p">)</span> <span class="o">+</span> <span class="mi">2</span><span class="o">*</span><span class="mi">2</span><span class="p">)</span><span class="o">*</span><span class="mi">2</span> <span class="o">+</span> <span class="mi">3</span><span class="o">*</span><span class="mi">2</span><span class="p">)</span><span class="o">*</span><span class="mi">2</span> <span class="o">+</span> <span class="mi">4</span><span class="o">*</span><span class="mi">2</span>
<span class="mi">44</span>
<span class="o">&gt;&gt;&gt;</span> 
</code></pre></div>

<h3 id="wraps-bao-liu-bei-zhuang-shi-han-shu-shu-xing"><a class="toclink" href="#wraps-bao-liu-bei-zhuang-shi-han-shu-shu-xing">wraps 保留被装饰函数属性</a></h3>
<p>wraps是一个便捷函数，实际是通过包中的另两个函数partial（这个另外讲）和update_wrapper（这个就略过了）实现的。这里就只讲wraps怎么用了。<br>
装饰器会掩盖掉被装饰函数的 <strong>name</strong> 和 <strong>doc</strong> 属性（不止这两个属性）。使用wraps装饰器，可以把相关的属性复制过去。<br>
这是一个拿来即用的装饰器，直接看一下使用前后的效果，示例代码：</p>
<div class="highlight"><pre><span></span><code><span class="kn">from</span> <span class="nn">functools</span> <span class="kn">import</span> <span class="n">wraps</span>

<span class="k">def</span> <span class="nf">empty_decorator</span><span class="p">(</span><span class="n">f</span><span class="p">):</span>
    <span class="c1">#@wraps(f)</span>
    <span class="k">def</span> <span class="nf">wrapper</span><span class="p">(</span><span class="o">*</span><span class="n">args</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">):</span>
        <span class="sd">&quot;&quot;&quot;这个装饰器什么都没做&quot;&quot;&quot;</span>
        <span class="k">return</span> <span class="n">f</span><span class="p">(</span><span class="o">*</span><span class="n">args</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">)</span>
    <span class="k">return</span> <span class="n">wrapper</span>

<span class="nd">@empty_decorator</span>
<span class="k">def</span> <span class="nf">hello</span><span class="p">():</span>
    <span class="sd">&quot;&quot;&quot;Hello World&quot;&quot;&quot;</span>
    <span class="nb">print</span><span class="p">(</span><span class="s2">&quot;Hello World!&quot;</span><span class="p">)</span>

<span class="k">if</span> <span class="vm">__name__</span> <span class="o">==</span> <span class="s2">&quot;__main__&quot;</span><span class="p">:</span>
    <span class="n">hello</span><span class="p">()</span>
    <span class="nb">print</span><span class="p">(</span><span class="n">hello</span><span class="o">.</span><span class="vm">__name__</span><span class="p">)</span>
    <span class="nb">print</span><span class="p">(</span><span class="n">hello</span><span class="o">.</span><span class="vm">__doc__</span><span class="p">)</span>
</code></pre></div>

<p>上面把<code>@wraps(f)</code>注释掉了，运行效果：</p>
<div class="highlight"><pre><span></span><code>Hello World!
wrapper
这个装饰器什么都没做
</code></pre></div>

<p>hello函数由于添加了装饰器，实际现在是装饰器函数wrapper，属性也都被替换掉了。  </p>
<p>然后放开注释再执行一次：</p>
<div class="highlight"><pre><span></span><code>Hello World!
hello
Hello World
</code></pre></div>

<p>加了<code>@wraps(f)</code>后，hello的属性都复制给了装饰器函数wrapper。  </p>
<h3 id="partial-partialmethod-dong-jie-can-shu"><a class="toclink" href="#partial-partialmethod-dong-jie-can-shu">partial partialmethod 冻结参数</a></h3>
<p>基于一个函数创建一个新的可调用对象，把原函数的某些参数固定。<br>
使用这个函数可以把接受一个或多个参数的函数，改编成需要回调的API，这样参数更少。  </p>
<p>用partial把一个两个参数的函数，改编成只需要一个参数的可调用对象：</p>
<div class="highlight"><pre><span></span><code><span class="o">&gt;&gt;&gt;</span> <span class="kn">from</span> <span class="nn">operator</span> <span class="kn">import</span> <span class="n">mul</span>
<span class="o">&gt;&gt;&gt;</span> <span class="kn">from</span> <span class="nn">functools</span> <span class="kn">import</span> <span class="n">partial</span>
<span class="o">&gt;&gt;&gt;</span> <span class="n">triple</span> <span class="o">=</span> <span class="n">partial</span><span class="p">(</span><span class="n">mul</span><span class="p">,</span> <span class="mi">3</span><span class="p">)</span>
<span class="o">&gt;&gt;&gt;</span> <span class="n">triple</span><span class="p">(</span><span class="mi">7</span><span class="p">)</span>
<span class="mi">21</span>
<span class="o">&gt;&gt;&gt;</span> <span class="p">[</span><span class="n">triple</span><span class="p">(</span><span class="n">i</span><span class="p">)</span> <span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="mi">10</span><span class="p">)]</span>
<span class="p">[</span><span class="mi">3</span><span class="p">,</span> <span class="mi">6</span><span class="p">,</span> <span class="mi">9</span><span class="p">,</span> <span class="mi">12</span><span class="p">,</span> <span class="mi">15</span><span class="p">,</span> <span class="mi">18</span><span class="p">,</span> <span class="mi">21</span><span class="p">,</span> <span class="mi">24</span><span class="p">,</span> <span class="mi">27</span><span class="p">]</span>
<span class="o">&gt;&gt;&gt;</span> 
</code></pre></div>

<p>还有一个partialmethod函数，行为类似partial，是用于处理方法的。  </p>
<h3 id="lru_cache-han-shu-ji-yi"><a class="toclink" href="#lru_cache-han-shu-ji-yi">lru_cache 函数记忆</a></h3>
<p>LRU是Least Recently Used的缩写。<br>
这是一个装饰器，可以缓存函数调用的结果，实现了函数记忆(memoization)的功能。在下一次以相同参数调用时直接返回上一次的结果。<br>
使用这个装饰器，可以优化程序，把耗时的函数结果保存起来，避免重复计算。<br>
由于是使用字典来存储缓存，函数的固定参数和关键字参数都必须是可哈希的。  </p>
<h4 id="shi-li-yan-shi"><a class="toclink" href="#shi-li-yan-shi">示例演示</a></h4>
<p>使用一个生成斐波那契数列的递归函数来演示效果，示例代码如下：</p>
<div class="highlight"><pre><span></span><code><span class="kn">import</span> <span class="nn">time</span>
<span class="kn">import</span> <span class="nn">functools</span>

<span class="k">def</span> <span class="nf">run_time</span><span class="p">(</span><span class="n">func</span><span class="p">):</span>
    <span class="nd">@functools</span><span class="o">.</span><span class="n">wraps</span><span class="p">(</span><span class="n">func</span><span class="p">)</span>
    <span class="k">def</span> <span class="nf">wrapper</span><span class="p">(</span><span class="o">*</span><span class="n">args</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">):</span>
        <span class="n">start</span> <span class="o">=</span> <span class="n">time</span><span class="o">.</span><span class="n">perf_counter</span><span class="p">()</span>
        <span class="n">result</span> <span class="o">=</span> <span class="n">func</span><span class="p">(</span><span class="o">*</span><span class="n">args</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">)</span>
        <span class="n">elapsed</span> <span class="o">=</span> <span class="n">time</span><span class="o">.</span><span class="n">perf_counter</span><span class="p">()</span> <span class="o">-</span> <span class="n">start</span>
        <span class="n">func_name</span> <span class="o">=</span> <span class="n">func</span><span class="o">.</span><span class="vm">__name__</span>
        <span class="n">arg_list</span> <span class="o">=</span> <span class="p">[]</span>
        <span class="k">if</span> <span class="n">args</span><span class="p">:</span>
            <span class="n">arg_list</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="s1">&#39;, &#39;</span><span class="o">.</span><span class="n">join</span><span class="p">(</span><span class="nb">repr</span><span class="p">(</span><span class="n">arg</span><span class="p">)</span> <span class="k">for</span> <span class="n">arg</span> <span class="ow">in</span> <span class="n">args</span><span class="p">))</span>
        <span class="k">if</span> <span class="n">kwargs</span><span class="p">:</span>
            <span class="n">pairs</span> <span class="o">=</span> <span class="p">[</span><span class="s1">&#39;</span><span class="si">%s</span><span class="s1">=</span><span class="si">%r</span><span class="s1">&#39;</span> <span class="o">%</span> <span class="p">(</span><span class="n">k</span><span class="p">,</span> <span class="n">w</span><span class="p">)</span> <span class="k">for</span> <span class="n">k</span><span class="p">,</span> <span class="n">w</span> <span class="ow">in</span> <span class="nb">sorted</span><span class="p">(</span><span class="n">kwargs</span><span class="o">.</span><span class="n">items</span><span class="p">())]</span>
            <span class="n">arg_list</span><span class="o">.</span><span class="n">extend</span><span class="p">(</span><span class="n">pairs</span><span class="p">)</span>
        <span class="n">arg_str</span> <span class="o">=</span> <span class="s1">&#39;, &#39;</span><span class="o">.</span><span class="n">join</span><span class="p">(</span><span class="n">arg_list</span><span class="p">)</span>
        <span class="nb">print</span><span class="p">(</span><span class="s1">&#39;[</span><span class="si">%.6f</span><span class="s1">s] </span><span class="si">%s</span><span class="s1">(</span><span class="si">%s</span><span class="s1">) -&gt; </span><span class="si">%r</span><span class="s1">&#39;</span> <span class="o">%</span> <span class="p">(</span><span class="n">elapsed</span><span class="p">,</span> <span class="n">func_name</span><span class="p">,</span> <span class="n">arg_str</span><span class="p">,</span> <span class="n">result</span><span class="p">))</span>
        <span class="k">return</span> <span class="n">result</span>
    <span class="k">return</span> <span class="n">wrapper</span>

<span class="c1">#@functools.lru_cache()</span>
<span class="nd">@run_time</span>
<span class="k">def</span> <span class="nf">fibnacci</span><span class="p">(</span><span class="n">n</span><span class="p">):</span>
    <span class="k">if</span> <span class="n">n</span> <span class="o">&lt;</span> <span class="mi">2</span><span class="p">:</span>
        <span class="k">return</span> <span class="n">n</span>
    <span class="k">return</span> <span class="n">fibnacci</span><span class="p">(</span><span class="n">n</span><span class="o">-</span><span class="mi">2</span><span class="p">)</span> <span class="o">+</span> <span class="n">fibnacci</span><span class="p">(</span><span class="n">n</span><span class="o">-</span><span class="mi">1</span><span class="p">)</span>

<span class="k">if</span> <span class="vm">__name__</span> <span class="o">==</span> <span class="s1">&#39;__main__&#39;</span><span class="p">:</span>
    <span class="nb">print</span><span class="p">(</span><span class="n">fibnacci</span><span class="p">(</span><span class="mi">5</span><span class="p">))</span>
</code></pre></div>

<p>这里用了一个装饰器run_time，可以显示函数调用的时间，调用的函数名称和参数，以及返回的结果。<br>
另外，用于优化的lru_cache装饰器，首先被注释掉了。  </p>
<p>优化前的执行结果如下：</p>
<div class="highlight"><pre><span></span><code>[0.000001s] fibnacci(1) -&gt; 1
[0.000001s] fibnacci(0) -&gt; 0
[0.000001s] fibnacci(1) -&gt; 1
[0.000572s] fibnacci(2) -&gt; 1
[0.001149s] fibnacci(3) -&gt; 2
[0.000001s] fibnacci(0) -&gt; 0
[0.000001s] fibnacci(1) -&gt; 1
[0.010550s] fibnacci(2) -&gt; 1
[0.000002s] fibnacci(1) -&gt; 1
[0.000001s] fibnacci(0) -&gt; 0
[0.000002s] fibnacci(1) -&gt; 1
[0.000569s] fibnacci(2) -&gt; 1
[0.001880s] fibnacci(3) -&gt; 2
[0.015456s] fibnacci(4) -&gt; 3
[0.017018s] fibnacci(5) -&gt; 5
5
</code></pre></div>

<p>这里的问题是fibnacci(1)被调用了5次，每次递归都会重复计算之前已经计算过的结果。  </p>
<p>需要有的地方把计算过的内容缓存下来，避免之后重复数计算。不用重复造轮子了。<br>
使用装饰器优化后的执行结果如下：</p>
<div class="highlight"><pre><span></span><code>[0.000001s] fibnacci(1) -&gt; 1
[0.000001s] fibnacci(0) -&gt; 0
[0.001100s] fibnacci(2) -&gt; 1
[0.011647s] fibnacci(3) -&gt; 2
[0.000003s] fibnacci(4) -&gt; 3
[0.012294s] fibnacci(5) -&gt; 5
5
</code></pre></div>

<p>这次避免了重复计算，执行速度可提升了。  </p>
<p>除了可以优化递归的算法，在从Web获取数据的场景里，也很有用。  </p>
<h4 id="can-shu-shuo-ming"><a class="toclink" href="#can-shu-shuo-ming">参数说明</a></h4>
<p>上面的示例中使用的都是默认参数。函数签名如下：</p>
<div class="highlight"><pre><span></span><code><span class="n">functools</span><span class="o">.</span><span class="n">lru_cache</span><span class="p">(</span><span class="n">maxsize</span><span class="o">=</span><span class="mi">128</span><span class="p">,</span> <span class="n">typed</span><span class="o">=</span><span class="kc">False</span><span class="p">)</span>
</code></pre></div>

<p>maxsize，指定存储多少个调用结果。存满之后，就的结果会被丢弃。数值设置为2的幂时可获得最佳性能。<br>
typed，如果设置为True，不同参数类型会分开保存。即把整数和浮点数（如3和3.0）区分开，分开缓存。  </p>
<h3 id="singledispatch-dan-fen-pai-fan-han-shu"><a class="toclink" href="#singledispatch-dan-fen-pai-fan-han-shu">singledispatch 单分派泛函数</a></h3>
<p>使用@singledispatch装饰的普通函数会变成单分派泛函数。<br>
单分派，根据第一个参数的类型，以不同的方式执行相同操作的一组函数。<em>如果是根据多个参数来决定的，就是多分派了。</em><br>
泛函数（generic function），把多个函数绑在一起组成一个泛函数。  </p>
<h4 id="shi-li-dai-ma"><a class="toclink" href="#shi-li-dai-ma">示例代码</a></h4>
<p>这里就直接把参数打印出来，根据参数类型的不同，调用不同的函数打印不同的格式:</p>
<div class="highlight"><pre><span></span><code><span class="kn">from</span> <span class="nn">functools</span> <span class="kn">import</span> <span class="n">singledispatch</span>

<span class="nd">@singledispatch</span>
<span class="k">def</span> <span class="nf">show</span><span class="p">(</span><span class="n">obj</span><span class="p">):</span>
    <span class="sd">&quot;&quot;&quot;基函数&quot;&quot;&quot;</span>
    <span class="k">return</span> <span class="s1">&#39;Unknow: </span><span class="si">%r</span><span class="s1">&#39;</span> <span class="o">%</span> <span class="n">obj</span>

<span class="nd">@show</span><span class="o">.</span><span class="n">register</span>
<span class="k">def</span> <span class="nf">_</span><span class="p">(</span><span class="n">arg</span><span class="p">:</span> <span class="nb">str</span><span class="p">):</span>
    <span class="k">return</span> <span class="s1">&#39;String: </span><span class="si">%s</span><span class="s1">&#39;</span> <span class="o">%</span> <span class="n">arg</span>

<span class="nd">@show</span><span class="o">.</span><span class="n">register</span>
<span class="k">def</span> <span class="nf">_</span><span class="p">(</span><span class="n">arg</span><span class="p">:</span> <span class="nb">int</span><span class="p">):</span>
    <span class="k">return</span> <span class="s1">&#39;Integral: </span><span class="si">%s</span><span class="s1">&#39;</span> <span class="o">%</span> <span class="n">arg</span>

<span class="nd">@show</span><span class="o">.</span><span class="n">register</span><span class="p">(</span><span class="nb">dict</span><span class="p">)</span>
<span class="k">def</span> <span class="nf">_</span><span class="p">(</span><span class="n">d</span><span class="p">):</span>
    <span class="k">return</span> <span class="s1">&#39;Dict: </span><span class="si">%s</span><span class="s1">&#39;</span> <span class="o">%</span> <span class="n">d</span>

<span class="nd">@show</span><span class="o">.</span><span class="n">register</span><span class="p">(</span><span class="nb">list</span><span class="p">)</span>
<span class="nd">@show</span><span class="o">.</span><span class="n">register</span><span class="p">(</span><span class="nb">set</span><span class="p">)</span>
<span class="k">def</span> <span class="nf">_</span><span class="p">(</span><span class="n">l</span><span class="p">):</span>
    <span class="k">return</span> <span class="s1">&#39;List/Set: </span><span class="si">%s</span><span class="s1">&#39;</span> <span class="o">%</span> <span class="n">l</span>

<span class="nd">@show</span><span class="o">.</span><span class="n">register</span><span class="p">(</span><span class="nb">tuple</span><span class="p">)</span>
<span class="k">def</span> <span class="nf">_</span><span class="p">(</span><span class="n">t</span><span class="p">):</span>
    <span class="k">return</span> <span class="s1">&#39;Tuple: </span><span class="si">%s</span><span class="s1">&#39;</span> <span class="o">%</span> <span class="p">(</span><span class="n">t</span><span class="p">,)</span>  <span class="c1"># 元祖不能直接用%s，这里的示例就是解决方法</span>

<span class="k">if</span> <span class="vm">__name__</span> <span class="o">==</span> <span class="s2">&quot;__main__&quot;</span><span class="p">:</span>
    <span class="nb">print</span><span class="p">(</span><span class="n">show</span><span class="p">(</span><span class="mi">123</span><span class="p">))</span>      <span class="c1"># Integral: 123</span>
    <span class="nb">print</span><span class="p">(</span><span class="n">show</span><span class="p">(</span><span class="mf">1.23</span><span class="p">))</span>     <span class="c1"># Unknow: 1.23</span>
    <span class="nb">print</span><span class="p">(</span><span class="n">show</span><span class="p">(</span><span class="s1">&#39;123&#39;</span><span class="p">))</span>    <span class="c1"># String: 123</span>
    <span class="nb">print</span><span class="p">(</span><span class="n">show</span><span class="p">([</span><span class="mi">1</span><span class="p">,</span><span class="mi">2</span><span class="p">,</span><span class="mi">3</span><span class="p">]))</span>  <span class="c1"># List/Set: [1, 2, 3]</span>
    <span class="nb">print</span><span class="p">(</span><span class="n">show</span><span class="p">({</span><span class="mi">1</span><span class="p">,</span><span class="mi">2</span><span class="p">,</span><span class="mi">3</span><span class="p">}))</span>  <span class="c1"># List/Set: {1, 2, 3}</span>
    <span class="nb">print</span><span class="p">(</span><span class="n">show</span><span class="p">((</span><span class="mi">1</span><span class="p">,</span><span class="mi">2</span><span class="p">,</span><span class="mi">3</span><span class="p">)))</span>  <span class="c1"># Tuple: (1, 2, 3)</span>
</code></pre></div>

<p>首先给基函数应用@singledispatch装饰器。<br>
各个专门函数使用@«base_function».register(«type»)装饰。还有一种不要参数利用类型标注的装饰方法。<br>
专门函数的名称无关紧要，这里都用了_作为函数名。<br>
每个需要特殊处理的类型都注册一个专门函数，多个类型也可以注册同一个专门函数，装饰器叠起来。  </p>
<p>注册专门函数的装饰器有两种用法。一种是不加参数，利用函数参数的类型标注。另一种是直接在装饰器中加参数指定。<br>
<em>没有参数的装饰器是嵌套了两层函数，有参数的装饰器是三层的嵌套。这个装饰器同时支持两种方式，很有趣，后面会讲实现方式。</em>  </p>
<h4 id="jian-yi-shi-yong-chou-xiang-ji-lei"><a class="toclink" href="#jian-yi-shi-yong-chou-xiang-ji-lei">建议使用抽象基类</a></h4>
<p>只要可能，注册的专门函数应该处理抽象基类，不要处理具体实现。这样，代码支持的兼容类型更广泛。<br>
上面用的都是具体实现而不是抽象基类。按照建议，int应该替换为numbers.Integral。list、dict、set这种可以在collections.abc模块里找，abc.MutableSequence、abc.MutableMapping、abc.MutableSet。<br>
使用抽象基类检查类型，可以让代码支持这些基类现有和未来的具体子类或虚拟子类。  </p>
<h4 id="shi-yong-singledispatch-de-you-shi"><a class="toclink" href="#shi-yong-singledispatch-de-you-shi">使用singledispatch的优势</a></h4>
<p>优点：支持模块化扩展，各个模块可以为它支持的各个类型注册一个专门的函数。<br>
可以在系统的任何地方和任何模块中注册专门函数。<br>
添加新类型，可以轻松扩展。<br>
可以为不是自己编写的或者不能修改的类添加自定义函数。  </p>
<h3 id="yi-ge-neng-jie-shou-ke-xuan-can-shu-de-zhuang-shi-qi"><a class="toclink" href="#yi-ge-neng-jie-shou-ke-xuan-can-shu-de-zhuang-shi-qi">一个能接收可选参数的装饰器</a></h3>
<p>这节要实现的装饰器是可以作为常规装饰器调用，不带参数也不带括号。也可以作为装饰器工厂函数调用，带括号并且括号内可以指定参数。也就是 @run_time 或 @run_time() 都可以。  </p>
<p>这里的装饰器函数有两种情况需要处理。第一种是只传入一个位置参数，并且这个参数是被装饰函数。另一种是传入的是可选参数。这里简化判断逻辑，限定可选参数必须用关键字参数传入。这样只要判断第一个位置参数是否为None，就可以区分两种情况了。  </p>
<h4 id="jin-xian-guan-jian-zi-can-shu"><a class="toclink" href="#jin-xian-guan-jian-zi-can-shu">仅限关键字参数</a></h4>
<p>定义函数时，可以指定仅限关键字参数。<br>
在函数签名中放一个*号，*号后面的参数就不支持位置参数，只能使用关键字参数:</p>
<div class="highlight"><pre><span></span><code><span class="o">&gt;&gt;&gt;</span> <span class="k">def</span> <span class="nf">f</span><span class="p">(</span><span class="n">x</span><span class="p">,</span> <span class="o">*</span><span class="p">,</span> <span class="n">y</span><span class="p">):</span>
    <span class="k">return</span> <span class="n">x</span><span class="p">,</span> <span class="n">y</span>

<span class="o">&gt;&gt;&gt;</span> <span class="n">f</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="n">y</span><span class="o">=</span><span class="mi">2</span><span class="p">)</span>
<span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="mi">2</span><span class="p">)</span>
<span class="o">&gt;&gt;&gt;</span> 
</code></pre></div>

<p>另外，Python3.8又加入了一个新特性，<strong>仅位置参数</strong>。用法就是用斜杠（\/）代替这里的星号。  </p>
<h4 id="dai-can-shu-de-run_time-zhuang-shi-qi"><a class="toclink" href="#dai-can-shu-de-run_time-zhuang-shi-qi">带参数的run_time装饰器</a></h4>
<p>这个装饰器的功能是可以输出被装饰函数的调用时间、函数名、参数和返回结果。并且提供一个参数，指定输出的格式。<br>
之后会基于这个装饰器进行修改：</p>
<div class="highlight"><pre><span></span><code><span class="kn">import</span> <span class="nn">time</span>
<span class="kn">import</span> <span class="nn">functools</span>

<span class="n">DEFAULT_FMT</span> <span class="o">=</span> <span class="s1">&#39;[</span><span class="si">{elapsed:.6f}</span><span class="s1">s] </span><span class="si">{func_name}</span><span class="s1">(</span><span class="si">{arg_str}</span><span class="s1">) -&gt; </span><span class="si">{result}</span><span class="s1">&#39;</span>

<span class="k">def</span> <span class="nf">run_time</span><span class="p">(</span><span class="n">fmt</span><span class="o">=</span><span class="n">DEFAULT_FMT</span><span class="p">):</span>
    <span class="k">def</span> <span class="nf">decorate</span><span class="p">(</span><span class="n">func</span><span class="p">):</span>
        <span class="nd">@functools</span><span class="o">.</span><span class="n">wraps</span><span class="p">(</span><span class="n">func</span><span class="p">)</span>
        <span class="k">def</span> <span class="nf">wrapper</span><span class="p">(</span><span class="o">*</span><span class="n">args</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">):</span>
            <span class="n">start</span> <span class="o">=</span> <span class="n">time</span><span class="o">.</span><span class="n">time</span><span class="p">()</span>
            <span class="n">result</span> <span class="o">=</span> <span class="n">func</span><span class="p">(</span><span class="o">*</span><span class="n">args</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">)</span>
            <span class="n">elapsed</span> <span class="o">=</span> <span class="n">time</span><span class="o">.</span><span class="n">time</span><span class="p">()</span> <span class="o">-</span> <span class="n">start</span>
            <span class="n">func_name</span> <span class="o">=</span> <span class="n">func</span><span class="o">.</span><span class="vm">__name__</span>
            <span class="n">arg_list</span> <span class="o">=</span> <span class="p">[]</span>
            <span class="k">if</span> <span class="n">args</span><span class="p">:</span>
                <span class="n">arg_list</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="s1">&#39;, &#39;</span><span class="o">.</span><span class="n">join</span><span class="p">(</span><span class="nb">repr</span><span class="p">(</span><span class="n">arg</span><span class="p">)</span> <span class="k">for</span> <span class="n">arg</span> <span class="ow">in</span> <span class="n">args</span><span class="p">))</span>
            <span class="k">if</span> <span class="n">kwargs</span><span class="p">:</span>
                <span class="n">pairs</span> <span class="o">=</span> <span class="p">[</span><span class="s1">&#39;</span><span class="si">%s</span><span class="s1">=</span><span class="si">%r</span><span class="s1">&#39;</span> <span class="o">%</span> <span class="p">(</span><span class="n">k</span><span class="p">,</span> <span class="n">w</span><span class="p">)</span> <span class="k">for</span> <span class="n">k</span><span class="p">,</span> <span class="n">w</span> <span class="ow">in</span> <span class="nb">sorted</span><span class="p">(</span><span class="n">kwargs</span><span class="o">.</span><span class="n">items</span><span class="p">())]</span>
                <span class="n">arg_list</span><span class="o">.</span><span class="n">extend</span><span class="p">(</span><span class="n">pairs</span><span class="p">)</span>
            <span class="n">arg_str</span> <span class="o">=</span> <span class="s1">&#39;, &#39;</span><span class="o">.</span><span class="n">join</span><span class="p">(</span><span class="n">arg_list</span><span class="p">)</span>
            <span class="nb">print</span><span class="p">(</span><span class="n">fmt</span><span class="o">.</span><span class="n">format</span><span class="p">(</span><span class="o">**</span><span class="nb">locals</span><span class="p">()))</span>
            <span class="k">return</span> <span class="n">result</span>
        <span class="k">return</span> <span class="n">wrapper</span>
    <span class="k">return</span> <span class="n">decorate</span>
</code></pre></div>

<p>随便写一个函数然后加上装饰器运行。下面的这个函数可以统计系统中某个文件夹的大小，输出参考Windows系统的文件夹属性里显示的格式（MB在Windows里应该是地板除，而我这里四舍五入，并且太大我这也不会变成GB），方便做对比和验证：</p>
<div class="highlight"><pre><span></span><code><span class="kn">import</span> <span class="nn">os</span>

<span class="nd">@run_time</span><span class="p">(</span><span class="s1">&#39;</span><span class="si">{elapsed:.4f}</span><span class="s1">s: </span><span class="si">{func_name}</span><span class="s1">(</span><span class="si">{arg_str}</span><span class="s1">) ==&gt; </span><span class="si">{result}</span><span class="s1">&#39;</span><span class="p">)</span>
<span class="k">def</span> <span class="nf">full_size</span><span class="p">(</span><span class="n">folder_path</span><span class="p">):</span>
    <span class="n">size</span> <span class="o">=</span> <span class="nb">sum</span><span class="p">(</span><span class="nb">sum</span><span class="p">(</span><span class="n">os</span><span class="o">.</span><span class="n">path</span><span class="o">.</span><span class="n">getsize</span><span class="p">(</span><span class="n">os</span><span class="o">.</span><span class="n">path</span><span class="o">.</span><span class="n">join</span><span class="p">(</span><span class="n">parent</span><span class="p">,</span> <span class="n">file</span><span class="p">))</span> \
        <span class="k">for</span> <span class="n">file</span> <span class="ow">in</span> <span class="n">files</span><span class="p">)</span> <span class="k">for</span> <span class="n">parent</span><span class="p">,</span> <span class="n">dirs</span><span class="p">,</span> <span class="n">files</span> <span class="ow">in</span> <span class="n">os</span><span class="o">.</span><span class="n">walk</span><span class="p">(</span><span class="n">folder_path</span><span class="p">))</span>
    <span class="k">return</span> <span class="sa">f</span><span class="s1">&#39;大小:  </span><span class="si">{</span><span class="n">size</span><span class="o">/</span><span class="mi">1024</span><span class="o">/</span><span class="mi">1024</span><span class="si">:</span><span class="s1">.3</span><span class="si">}</span><span class="s1"> MB (</span><span class="si">{</span><span class="n">size</span><span class="si">:</span><span class="s1">,</span><span class="si">}</span><span class="s1"> 字节)&#39;</span>

<span class="k">if</span> <span class="vm">__name__</span> <span class="o">==</span> <span class="s1">&#39;__main__&#39;</span><span class="p">:</span>
    <span class="n">full_size</span><span class="p">(</span><span class="sa">r</span><span class="s1">&#39;D:/Python&#39;</span><span class="p">)</span>
    <span class="c1"># 0.2709s: full_size(&#39;D:/Python&#39;) ==&gt; 大小:  88.5 MB (92,752,771 字节)</span>
</code></pre></div>

<p>故意只用了一行就实现了，嵌套比较多，可读性差。不过使用的sum函数和生成器表达式都是之前建议的做法。  </p>
<h4 id="xiu-gai-run_time-zhuang-shi-qi"><a class="toclink" href="#xiu-gai-run_time-zhuang-shi-qi">修改run_time装饰器</a></h4>
<p>这里的修改很简单，因为处理的逻辑已经限制的很好了。修改后的装饰器如下：</p>
<div class="highlight"><pre><span></span><code><span class="kn">import</span> <span class="nn">time</span>
<span class="kn">import</span> <span class="nn">functools</span>

<span class="n">DEFAULT_FMT</span> <span class="o">=</span> <span class="s1">&#39;[</span><span class="si">{elapsed:.6f}</span><span class="s1">s] </span><span class="si">{func_name}</span><span class="s1">(</span><span class="si">{arg_str}</span><span class="s1">) -&gt; </span><span class="si">{result}</span><span class="s1">&#39;</span>

<span class="k">def</span> <span class="nf">run_time</span><span class="p">(</span><span class="n">func</span><span class="o">=</span><span class="kc">None</span><span class="p">,</span> <span class="o">*</span><span class="p">,</span> <span class="n">fmt</span><span class="o">=</span><span class="n">DEFAULT_FMT</span><span class="p">):</span>
    <span class="k">if</span> <span class="n">func</span> <span class="ow">is</span> <span class="kc">None</span><span class="p">:</span>
        <span class="k">return</span> <span class="n">functools</span><span class="o">.</span><span class="n">partial</span><span class="p">(</span><span class="n">run_time</span><span class="p">,</span> <span class="n">fmt</span><span class="o">=</span><span class="n">fmt</span><span class="p">)</span>

    <span class="nd">@functools</span><span class="o">.</span><span class="n">wraps</span><span class="p">(</span><span class="n">func</span><span class="p">)</span>
    <span class="k">def</span> <span class="nf">wrapper</span><span class="p">(</span><span class="o">*</span><span class="n">args</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">):</span>
        <span class="n">start</span> <span class="o">=</span> <span class="n">time</span><span class="o">.</span><span class="n">time</span><span class="p">()</span>
        <span class="n">result</span> <span class="o">=</span> <span class="n">func</span><span class="p">(</span><span class="o">*</span><span class="n">args</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">)</span>
        <span class="n">elapsed</span> <span class="o">=</span> <span class="n">time</span><span class="o">.</span><span class="n">time</span><span class="p">()</span> <span class="o">-</span> <span class="n">start</span>
        <span class="n">func_name</span> <span class="o">=</span> <span class="n">func</span><span class="o">.</span><span class="vm">__name__</span>
        <span class="n">arg_list</span> <span class="o">=</span> <span class="p">[]</span>
        <span class="k">if</span> <span class="n">args</span><span class="p">:</span>
            <span class="n">arg_list</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="s1">&#39;, &#39;</span><span class="o">.</span><span class="n">join</span><span class="p">(</span><span class="nb">repr</span><span class="p">(</span><span class="n">arg</span><span class="p">)</span> <span class="k">for</span> <span class="n">arg</span> <span class="ow">in</span> <span class="n">args</span><span class="p">))</span>
        <span class="k">if</span> <span class="n">kwargs</span><span class="p">:</span>
            <span class="n">pairs</span> <span class="o">=</span> <span class="p">[</span><span class="s1">&#39;</span><span class="si">%s</span><span class="s1">=</span><span class="si">%r</span><span class="s1">&#39;</span> <span class="o">%</span> <span class="p">(</span><span class="n">k</span><span class="p">,</span> <span class="n">w</span><span class="p">)</span> <span class="k">for</span> <span class="n">k</span><span class="p">,</span> <span class="n">w</span> <span class="ow">in</span> <span class="nb">sorted</span><span class="p">(</span><span class="n">kwargs</span><span class="o">.</span><span class="n">items</span><span class="p">())]</span>
            <span class="n">arg_list</span><span class="o">.</span><span class="n">extend</span><span class="p">(</span><span class="n">pairs</span><span class="p">)</span>
        <span class="n">arg_str</span> <span class="o">=</span> <span class="s1">&#39;, &#39;</span><span class="o">.</span><span class="n">join</span><span class="p">(</span><span class="n">arg_list</span><span class="p">)</span>
        <span class="nb">print</span><span class="p">(</span><span class="n">fmt</span><span class="o">.</span><span class="n">format</span><span class="p">(</span><span class="o">**</span><span class="nb">locals</span><span class="p">()))</span>
        <span class="k">return</span> <span class="n">result</span>
    <span class="k">return</span> <span class="n">wrapper</span>
</code></pre></div>

<p>变回二层的装饰器，在开头加一个if判断并返回一条语句。代码很公式化，基本都可以套用。代价就是所有的选项限定只能用关键字参数。  </p>
<h4 id="wan-cheng-run_time-zhuang-shi-qi"><a class="toclink" href="#wan-cheng-run_time-zhuang-shi-qi">完成run_time装饰器</a></h4>
<p>上一节使用的方式是最通用的，处理逻辑比较简单。如果不想限定只能用关键字参数，也是可以的。不过逻辑会复杂一些，并且通用性也差。<br>
这里的要用的判断逻辑是第一个位置参数是否可调用（callable）。这限定了不考虑接受函数为参数的情况。<br>
这个参数有4种情况：</p>
<ol>
<li>不带括号的调用。<code>@run_time</code></li>
<li>使用括号调用，但是没有任何参数。<code>@run_time()</code></li>
<li>使用括号调用，用了关键字参数。<code>@run_time(fmt='{elapsed:.4f}s {func_name}({arg_str}) &gt; {result}')</code></li>
<li>使用括号调用，用了位置参数。<code>@run_time('{elapsed:.4f}s {func_name}({arg_str}) &gt;&gt; {result}')</code></li>
</ol>
<p>这里比上一节的例子多了一个用位置参数传参的情况，这样就会出现参数的错位，需要额外处理。<br>
下面是最终完成的装饰器版本了：</p>
<div class="highlight"><pre><span></span><code><span class="kn">import</span> <span class="nn">time</span>
<span class="kn">import</span> <span class="nn">functools</span>

<span class="n">DEFAULT_FMT</span> <span class="o">=</span> <span class="s1">&#39;[</span><span class="si">{elapsed:.6f}</span><span class="s1">s] </span><span class="si">{func_name}</span><span class="s1">(</span><span class="si">{arg_str}</span><span class="s1">) -&gt; </span><span class="si">{result}</span><span class="s1">&#39;</span>

<span class="k">def</span> <span class="nf">run_time</span><span class="p">(</span><span class="n">func</span><span class="o">=</span><span class="kc">None</span><span class="p">,</span> <span class="n">fmt</span><span class="o">=</span><span class="n">DEFAULT_FMT</span><span class="p">):</span>
    <span class="k">if</span> <span class="ow">not</span> <span class="n">callable</span><span class="p">(</span><span class="n">func</span><span class="p">):</span>
        <span class="k">if</span> <span class="n">func</span> <span class="ow">is</span> <span class="ow">not</span> <span class="kc">None</span><span class="p">:</span>
            <span class="n">fmt</span> <span class="o">=</span> <span class="n">func</span>
        <span class="k">return</span> <span class="n">functools</span><span class="o">.</span><span class="n">partial</span><span class="p">(</span><span class="n">run_time</span><span class="p">,</span> <span class="n">fmt</span><span class="o">=</span><span class="n">fmt</span><span class="p">)</span>

    <span class="nd">@functools</span><span class="o">.</span><span class="n">wraps</span><span class="p">(</span><span class="n">func</span><span class="p">)</span>
    <span class="k">def</span> <span class="nf">wrapper</span><span class="p">(</span><span class="o">*</span><span class="n">args</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">):</span>
        <span class="n">start</span> <span class="o">=</span> <span class="n">time</span><span class="o">.</span><span class="n">time</span><span class="p">()</span>
        <span class="n">result</span> <span class="o">=</span> <span class="n">func</span><span class="p">(</span><span class="o">*</span><span class="n">args</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">)</span>
        <span class="n">elapsed</span> <span class="o">=</span> <span class="n">time</span><span class="o">.</span><span class="n">time</span><span class="p">()</span> <span class="o">-</span> <span class="n">start</span>
        <span class="n">func_name</span> <span class="o">=</span> <span class="n">func</span><span class="o">.</span><span class="vm">__name__</span>
        <span class="n">arg_list</span> <span class="o">=</span> <span class="p">[]</span>
        <span class="k">if</span> <span class="n">args</span><span class="p">:</span>
            <span class="n">arg_list</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="s1">&#39;, &#39;</span><span class="o">.</span><span class="n">join</span><span class="p">(</span><span class="nb">repr</span><span class="p">(</span><span class="n">arg</span><span class="p">)</span> <span class="k">for</span> <span class="n">arg</span> <span class="ow">in</span> <span class="n">args</span><span class="p">))</span>
        <span class="k">if</span> <span class="n">kwargs</span><span class="p">:</span>
            <span class="n">pairs</span> <span class="o">=</span> <span class="p">[</span><span class="s1">&#39;</span><span class="si">%s</span><span class="s1">=</span><span class="si">%r</span><span class="s1">&#39;</span> <span class="o">%</span> <span class="p">(</span><span class="n">k</span><span class="p">,</span> <span class="n">w</span><span class="p">)</span> <span class="k">for</span> <span class="n">k</span><span class="p">,</span> <span class="n">w</span> <span class="ow">in</span> <span class="nb">sorted</span><span class="p">(</span><span class="n">kwargs</span><span class="o">.</span><span class="n">items</span><span class="p">())]</span>
            <span class="n">arg_list</span><span class="o">.</span><span class="n">extend</span><span class="p">(</span><span class="n">pairs</span><span class="p">)</span>
        <span class="n">arg_str</span> <span class="o">=</span> <span class="s1">&#39;, &#39;</span><span class="o">.</span><span class="n">join</span><span class="p">(</span><span class="n">arg_list</span><span class="p">)</span>
        <span class="nb">print</span><span class="p">(</span><span class="n">fmt</span><span class="o">.</span><span class="n">format</span><span class="p">(</span><span class="o">**</span><span class="nb">locals</span><span class="p">()))</span>
        <span class="k">return</span> <span class="n">result</span>
    <span class="k">return</span> <span class="n">wrapper</span>
</code></pre></div>

<p>这样的处理方式没有通用性，这里是因为参数少才方便进行判断和操作。之后如果还需要为装饰器添加功能，新加入的关键字还是需要用上仅限关键字参数。  </p>
      </div>
      <div class="back-to-top">
        <a href="/">HOME</a>
        <a href="#top">TOP</a>
      </div>
    </article>
  </div>
<!-- end article -->
<!-- 页面往下滚动一段之后才会显示TOC -->
<script>
  window.onscroll = function() {
    var tocbox = document.getElementsByClassName('toc')[0];
    var osTop = document.documentElement.scrollTop || document.body.scrollTop;
    var osWidth = document.documentElement.scrollWidth || document.body.scrollWidth;
    // console.log(osTop)
    if (osTop>300 && osWidth>865) {
      tocbox.style.display = "block"
    }
    if (osTop<300 || osWidth<865) {
      tocbox.style.display = "none"
    }
  }
</script>
                <footer>
                    <div class="icons">
                    </div>
                    <span id="busuanzi_container_page_pv" style="padding: 10px">本文阅读量<span id="busuanzi_value_page_pv"></span>次</span>
                    <span id="busuanzi_container_site_pv" style="padding: 10px">本站总访问量<span id="busuanzi_value_site_pv"></span>次</span>
                    <span id="busuanzi_container_site_uv" style="padding: 10px">本站总访客数<span id="busuanzi_value_site_uv"></span>人</span>
                    <p>© <script>document.write(moment().format('YYYY'));</script> 749B</p>
                </footer>
        </div>
</body>
</html>